#include "stdafx.h"
#include "solver.h"
#include "MLP.h"
#include "layer.h"
#include "neuron.h"
#include "dendrite.h"
#include "Neuron Form.h"

namespace nnse
{
	extern io_module io_m;

	// Constructor and Destructor

	template <class T, class U> neuron<T, U>::neuron(layer<T, U>^ p, unsigned int n_n)
	: parent(p), number(n_n)
	{
		io_m.startup_stream << "Constructing neuron " << get_number() << std::endl;
		NET=0.0;
		OUT=0.0;
		ERR=0.0;
		DELTA=0.0;
		number_of_dendrites=0;
		dendrites=nullptr;
		neuron_form=nullptr;
	}

	template <class T, class U> neuron<T, U>::~neuron()
	{
		io_m.startup_stream << "Destructing neuron " << get_number() << std::endl;
	}

	// Accessor Functions

	template <class T, class U> inline problem<T, U>^ neuron<T, U>::get_parent_problem()
	{
		return get_parent_layer()->get_parent_MLP()->get_parent_solver()->get_parent_problem();
	}

	template <class T, class U> inline solver<T, U>^ neuron<T, U>::get_parent_solver()
	{
		return get_parent_layer()->get_parent_MLP()->get_parent_solver();
	}

	template <class T, class U> inline MLP<T, U>^ neuron<T, U>::get_parent_MLP()
	{
		return get_parent_layer()->get_parent_MLP();
	}

	template <class T, class U> inline layer<T, U>^ neuron<T, U>::get_parent_layer()
	{
		return parent;
	}

	template <class T, class U> inline unsigned int neuron<T, U>::get_number()
	{
		return number;
	}

	template <class T, class U> inline void neuron<T, U>::put_number(unsigned int n)
	{
		number=n;
	}

	template <class T, class U> inline U neuron<T, U>::get_NET()
	{
		return NET;
	}

	template <class T, class U> inline void neuron<T, U>::put_NET()
	{
		NET=calc_NET();
		io_m.results_stream << "NEW NEURON NET: " << NET << std::endl;
		if (neuron_form)
		{
			neuron_form->update_form();
		}
	}

	template <class T, class U> inline U neuron<T, U>::get_OUT()
	{
		return OUT;
	}

	template <class T, class U> inline void neuron<T, U>::put_OUT()
	{
		OUT=calc_OUT();
		io_m.results_stream << "NEW NEURON OUT: " << OUT << std::endl;
		if (neuron_form)
		{
			neuron_form->update_form();
		}
	}

	template <class T, class U> inline U neuron<T, U>::get_ERR()
	{
		return ERR;
	}

	template <class T, class U> inline void neuron<T, U>::put_ERR(layer<T, U>% next_layer)
	{
		ERR=calc_ERR(next_layer);
		io_m.results_stream << "NEW NEURON ERR: " << ERR << std::endl;
		if (neuron_form)
		{
			neuron_form->update_form();
		}
	}

	template <class T, class U> inline U neuron<T, U>::get_DELTA()
	{
		return DELTA;
	}

	template <class T, class U> inline void neuron<T, U>::put_DELTA()
	{
		DELTA=calc_DELTA();
		io_m.results_stream << "NEW NEURON DELTA: " << DELTA << std::endl;
		if (neuron_form)
		{
			neuron_form->update_form();
		}
	}

	template <class T, class U> inline unsigned int neuron<T, U>::get_number_of_dendrites()
	{
		return number_of_dendrites;
	}

	template <class T, class U> inline void neuron<T, U>::put_number_of_dendrites(unsigned int n_d)
	{
		number_of_dendrites=n_d;
	}

	template <class T, class U> inline dendrite<T, U>% neuron<T, U>::get_dendrite(unsigned int d_n)
	{
		if (dendrites && d_n>=0 && d_n<dendrites->Length)
		{
			return *dendrites[d_n];
		}
		else
		{
			throw std::out_of_range("\nError in neuron<T, U>::get_dendrite");
		}
	}

	template <class T, class U> void neuron<T, U>::put_dendrites()
	{
		dendrites=gcnew array<dendrite<T, U>^>(get_number_of_dendrites());
		for (unsigned int d_n=0; d_n<get_number_of_dendrites(); d_n++)
		{
			dendrites[d_n]=gcnew dendrite<T, U>(this, d_n);
		}
	}

	// Worker Functions

	template <class T, class U> void neuron<T, U>::forward(layer<T, U>% next_layer, bool update_flag)
	{
		if (update_flag)
		{
			update_fanout_weights(next_layer);
		}
		put_NET();
		put_OUT();
		pass_on_OUT(next_layer);
	}

	template <class T, class U> U neuron<T, U>::reverse(layer<T, U>% next_layer)
	{
		put_ERR(next_layer);
		put_DELTA();
		filter_back_DELTA();
		return 0.0;
	}

	template <class T, class U> U neuron<T, U>::calc_NET()
	{
		U NET=0.0;
		for (unsigned int d_n=0; d_n<get_number_of_dendrites(); d_n++)
		{
			NET+=get_dendrite(d_n).get_output();
		}
		return NET;
	}

	template <class T, class U> U neuron<T, U>::calc_OUT()
	{
		return 1.0/(1.0+exp(-get_NET()));
	}

	template <class T, class U> U neuron<T, U>::calc_ERR(layer<T, U>% next_layer)
	{
		U ERR=0.0;
		for (unsigned int n_n=0; n_n<next_layer.get_number_of_neurons(); n_n++)
		{
			ERR+=next_layer.get_neuron(n_n).get_dendrite(get_number()).get_output();
		}
		return ERR;
	}

	template <class T, class U> U neuron<T, U>::calc_DELTA()
	{
		 return get_OUT()*(1.0-get_OUT())*get_ERR();
	}

	template <class T, class U> void neuron<T, U>::update_fanout_weights(layer<T, U>% next_layer)
	{
		for (unsigned int n_n=0; n_n<next_layer.get_number_of_neurons(); n_n++)
		{
			next_layer.get_neuron(n_n).update_fanin_weight(get_number(), get_OUT());
		}
	}

	template <class T, class U> void neuron<T, U>::update_fanin_weight(unsigned int d_n, U OUT)
	{
		get_dendrite(d_n).update_weight(OUT, get_DELTA());
	}

	template <class T, class U> void neuron<T, U>::pass_on_OUT(layer<T, U>% next_layer)
	{
		for (unsigned int n_n=0; n_n<next_layer.get_number_of_neurons(); n_n++)
		{
			next_layer.get_neuron(n_n).get_dendrite(get_number()).put_input(get_OUT());
		}
	}

	template <class T, class U> void neuron<T, U>::filter_back_DELTA()
	{
		for (unsigned int d_n=0; d_n<get_number_of_dendrites(); d_n++)
		{
			get_dendrite(d_n).put_input(get_DELTA());
		}
	}

	// GUI Functions

	template <class T, class U> void neuron<T, U>::display_form()
	{
		if (!neuron_form)
		{
			neuron_form=gcnew SimpleGUI::Neuron<T, U>(this);
		}
		neuron_form->Show();
		neuron_form->update_form();
	}

	template <class T, class U> void neuron<T, U>::invalidate_form()
	{
		neuron_form=nullptr;
	}

	template <class T, class U> void neuron<T, U>::display_dendrites()
	{
		for (unsigned int d_n=0; d_n<get_number_of_dendrites(); d_n++)
		{
			get_dendrite(d_n).display_form();
		}
	}

	// Serialisation Functions

	template <class T, class U> void neuron<T, U>::read(std::wifstream& in)
	{
		unsigned int number;
		in >> number;
		put_number(number);
		U NET;
		in >> NET;
		this->NET=NET;
		U OUT;
		in >> OUT;
		this->OUT=OUT;
		U ERR;
		in >> ERR;
		this->ERR=ERR;
		U DELTA;
		in >> DELTA;
		this->DELTA=DELTA;
		unsigned int number_of_dendrites;
		in >> number_of_dendrites;
		put_number_of_dendrites(number_of_dendrites);
		for (unsigned int d_n=0; d_n<get_number_of_dendrites(); d_n++)
		{
			in >> get_dendrite(d_n);
		}
	}

	template <class T, class U> void neuron<T, U>::write(std::wofstream& out)
	{
		out << get_number() << " ";
		out << get_NET() << " ";
		out << get_OUT() << " ";
		out << get_ERR() << " ";
		out << get_DELTA() << " ";
		out << get_number_of_dendrites() << " ";
		for (unsigned int d_n=0; d_n<get_number_of_dendrites(); d_n++)
		{
			out << get_dendrite(d_n);
		}
	}

	template <class T, class U> void neuron<T, U>::display(std::wofstream& out)
	{
		out << "Number: " << get_number() << std::endl;
		out << "NET: " << get_NET() << std::endl;
		out << "OUT: " << get_OUT() << std::endl;
		out << "ERR: " << get_ERR() << std::endl;
		out << "DELTA: " << get_DELTA() << std::endl;
		out << "Number of Dendrites: " << get_number_of_dendrites() << std::endl;
		for (unsigned int d_n=0; d_n<get_number_of_dendrites(); d_n++)
		{
			out << get_dendrite(d_n);
		}
	}

	template <class T, class U> std::wifstream& operator>>(std::wifstream& in, neuron<T, U>% n)
	{
		if (in.good())
		{
			n.read(in);
			return in;
		}
		else
		{
			throw std::ios_base::failure("\nError in wifstream& >> neuron<T, U>%");
		}
	}

	template <class T, class U> std::wofstream& operator<<(std::wofstream& out, neuron<T, U>% n)
	{
		if (out.good())
		{
			n.write(out);
			return out;
		}
		else
		{
			throw std::ios_base::failure("\nError in wofstream& << neuron<T, U>%");
		}
	}

	// Explicit Instantiation

	template ref class neuron<bool, double>;
	template ref class neuron<unsigned char, double>;
	template std::wifstream& operator>>(std::wifstream& in, neuron<bool, double>% n);
	template std::wifstream& operator>>(std::wifstream& in, neuron<unsigned char, double>% n);
	template std::wofstream& operator<<(std::wofstream& out, neuron<bool, double>% n);
	template std::wofstream& operator<<(std::wofstream& out, neuron<unsigned char, double>% n);
}
